home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 22 / Cream of the Crop 22.iso / os2 / bltc127.zip / !README < prev    next >
Text File  |  1996-11-12  |  29KB  |  621 lines

  1. --------------------------------
  2. Read me first (sharware release)
  3. BULLET v1.2
  4.  
  5. --Documentation (read this before using CZ)
  6.  
  7. (Any reference to the separate version needed for Borland C/C++ compilers
  8. has been superseded -- the single .LIB works for any standard object
  9. module compiler.)
  10.  
  11. The documentation is online in the CZ.COM/CZ.HLP files. To get started just
  12. make sure CZ.HLP is in the current directory and load CZ:
  13.  
  14.    C>cz
  15.  
  16. It can also be loaded high.
  17.  
  18. Caution:  You will not be able to use CZ very well unless you read
  19.           'Using_CZ' in the TUTORIAL_INDEX.  Spend 5 minutes there
  20.           can save you several hours, even more, in the long run.
  21.           There's nothing mystical about it, if you know how to
  22.           make it do what you want.
  23.  
  24. Press Alt-F1 to hotkey into CZ. Press END (cursor moves to TUTORIAL_INDEX).
  25. Press ENTER. Move the cursor to under Starting_CZ. Press ENTER. Use the
  26. END/ENTER combo to continue until you get the idea. A mouse makes using
  27. CZ even easier.
  28.  
  29. If you want to have a hardcopy of the CZ.HLP use the HLP2TXT.EXE program to
  30. strip off the header info. Load it into your favorite WP or text editor.
  31.  
  32. This package is documentation/source-specific to DOS C and C++ compilers in
  33. that much of the source examples deal with using BULLET under these compilers.
  34. Because of this, this version is called BLTCxxx.ZIP.
  35.  
  36.  
  37. --Installation
  38.  
  39. BULLET comes with the BULLET.LIB in compressed form (BULLET.LI_). It must be
  40. installed using the INSTALL.EXE program. INSTALL performs one function: it
  41. decompresses BULLET.LI_ (to BULLET.LIB) and writes installation information
  42. to the BULLET.LIB file. It does nothing else.
  43.  
  44. To install:
  45.  
  46.    C>install
  47.  
  48. The installation process asks two questions and then decompresses the file so
  49. that BULLET.LIB is ready for use. BULLET.LIB is fully-functional and not
  50. crippled. You may use BULLET.LIB and the BULLET package for evaluation only and
  51. for an evaluation period not to exceed 21 days from the date of installation.
  52. BULLET.LIB works with just about every DOS compiler on the market.
  53.  
  54.  
  55. --Registration
  56.  
  57. BULLET is a shareware product. This means that you may try BULLET during the
  58. evaluation period free of obligation. However, you may not use BULLET after
  59. this evaluation period without registering--you must purchase a license to
  60. use BULLET if you wish to continue to use BULLET or any programs produced with
  61. BULLET. See the !ORDER.FRM for ordering information by check, cash, or money
  62. order. Use !ORDER.CC for ordering by credit card.
  63.  
  64. For alternate payment options, see !ORDER.BMT.
  65.  
  66.  
  67. --Upgrades and Updates, Site licensing
  68.  
  69. All updates and upgrades are available from the support BBS free-of-charge.
  70. See the PRODUCT_SUPPORT heading under TUTORIAL_INDEX in CZ for more.
  71.  
  72. Site licensing is available at reasonable rates. Contact me for details.
  73.  
  74.  
  75. --Distribution
  76.  
  77. BBS Sysops may carry BULLET on their BBS provided that all files listed in
  78. the !PACKLST.TXT file are included in the archive. This file must be provided
  79. free of charge, i.e., this file cannot placed in an area that requires users
  80. to pay a fee for per-file download. A general subscription fee BBS is exempt
  81. from this limitation, provided that no specific charge is assessed to this
  82. particular file.
  83.  
  84. Shareware distributors/catalog vendors may carry BULLET on their inventory
  85. provided that: the BULLET package be the only software on the diskette, that
  86. it be made clear that the purchase of the disk does not constitute purchase
  87. of the product, and that no charge greater than a reasonable amount for
  88. distribution services be charged (for example, not more than 5 dollars).
  89. CD-ROM vendors may include BULLET and may charge more than $5 for the CD,
  90. provided that the CD is not for the sole or primary purpose of distributing
  91. Bullet.
  92.  
  93. Only the shareware version of Bullet may be distributed without explicit
  94. permission from the author.
  95.  
  96. In any case, the author of BULLET may at anytime request (and must be granted)
  97. that the BULLET package be no longer advertised/distributed in the vendors
  98. catalog and that within a reasonable time after request, advertising of BULLET
  99. will end (within 6 months and distribution will end immediately). (Basically,
  100. this means that you as a shareware vendor may include BULLET in your
  101. advertising/distributing but that that I, as the author and owner of BULLET,
  102. have the option of having you stop advertising/distributing BULLET, for
  103. whatever reason (such as it becoming a non-shareware commercial product).)
  104.  
  105.  
  106. --------------------------------------------------------
  107. Status value return code with transaction-based routines
  108.  
  109. The transaction-based routines, InsertXB, UpdateXB, ReindexXB, LockXB and
  110. UnlockXB, do not return the completion status. The return value is the
  111. pack index number that failed. The following example assumes that you are
  112. using multiple index files. If you're not, replace AP(x).y with AP.y.
  113.  
  114.    AP[0].Func = INSERTXB;
  115.    pid = BULLET(&AP);
  116.    if (pid == 0) {
  117.       if (AP[0].stat == 0) {
  118.          /* all okay */
  119.       else {                  /* error while adding the data record is       */
  120.          rstat = AP[0].stat;  /* returned in AP(0).stat if and only if pid=0 */
  121.          DoErrorWithDataRecord(stat);
  122.          }
  123.       }
  124.    else {
  125.       rstat = AP[pid-1].stat;             /* -1 since C arrays are 0-based */
  126.       DoErrorWithIndexFile(pid-1, stat);  /* and pid is 1-based */
  127.       }
  128.    }
  129.  
  130. For complete information consult the above named routines in CZ. Note that
  131. ReindexXB does not operate on a data file separately so if its return
  132. code=0 (pid=0) then all operations succeeded. However, with the LockXB
  133. routines, if pid > number_of_packs then the lock on the data file failed.
  134. The following example assumes that two index files and a data file are
  135. to be locked.
  136.  
  137.    packs = 2;            /* 2 packs (two index files per data file) */
  138.    AP[0].Func = LOCKXB;  /* assume AP[1] also done via .nextPtr */
  139.    pid = BULLET(&AP);
  140.    if (pid == 0) {
  141.       /* all okay */
  142.    else
  143.       if (pid <= packs) {   /* error while locking index file and error code */
  144.          rstat = AP[pid-1].stat;     /* in AP[pid-1].stat */
  145.          DoErrorWithIndexFile(stat);
  146.          }
  147.       else {                /* error while locking data file and error code */
  148.          stat = AP[0].stat;    /* in FIRST AP[].stat  ([0] is first, [1] last */
  149.          DoErrorWithDataFile(stat); /* i.e., if here then pid=3 (yes) */
  150.       }
  151.    }
  152.  
  153. --In case a C example does not compile, do not assume that the example is 100%
  154. correct C and that something must be wrong. I'm no C programmer--not by a mile.
  155. If something doesn't compile make the needed corrections. Let me know about my
  156. mistakes and I will be ever so grateful. -chh  (That was written about 4 years
  157. ago, as were just about all the example code here -- 12-Nov-1996 -chh.)
  158.  
  159. ---------------
  160. Troubleshooting
  161.  
  162. Common problems encountered by new users of Bullet are:
  163.  
  164. Q: Common problem?
  165. A: Be sure to memset (set to 0) any unused data bytes.
  166.  
  167. P: Data fields are skewed, with some fields containing
  168. parts of other fields' data.
  169.  
  170. S: Most of these problems relate to the way C strings are
  171. 0-terminated, while dBASE DBF fields are fixed-length,
  172. and so need not have the \0.  If you are going to use
  173. strcpy, or any other C run-time library that requires
  174. null-terminated strings, then be sure to provide for this
  175. when you define your DBF record.  For example, if you
  176. want two fields, LASTN and FIRSTN, to each have 15
  177. characters, but will be using C strings, define the
  178. fields as 16.  This will allow room for the \0 on your
  179. strings, and give 15 true character places.
  180.  
  181. Often, the case is such that all appears normal when the
  182. DBF file is accessed (i.e., the data fields don't appear
  183. to be skewed), but indexed access is not correct.  This
  184. is because your program code has templated the data
  185. record consistent with what was written (and is read) at
  186. the DBF level.  However, it is not consistent with the
  187. DBF header specifications (written in CreateDXB) and so
  188. any external access to the DBF (including Bullet keyed
  189. access) will not be correct.
  190.  
  191. Skewed data also results when you change either your
  192. program's data structure template of the DBF record, or
  193. your DBF field list definitions, but don't do the same to
  194. both.  The physical record buffer in your program must
  195. exactly match that of the DBF record.  Off the subject a
  196. bit, but possibly on your mind:  yes, you can create all
  197. this on the fly (i.e., not hard-coded fieldnames, etc.),
  198. but it does require that you manage all offsets yourself,
  199. rather than have the compiler do it for you.  A job for a
  200. C++ wrapper, I suppose.  Certainly do'able as I''ve done
  201. it using a BASIC compiler (see IDEMO.*)
  202.  
  203. Another reason for skewed fields is that the compiler is
  204. modifying the alignment of your program's DBF record
  205. structure.  It is imperative that you instruct your
  206. compiler to not modify any Bullet-used structure:
  207. #pragma pack() is used by many compilers.  Consult your
  208. compiler documentation.  For non-Bullet structures,
  209. alignment doesn't matter to Bullet.  Bullet structures
  210. are any of the Bullet packs, and any DBF record
  211. template/buffer/structure that your program uses to
  212. communicate with Bullet.
  213.  
  214. The main point is to ensure that your physical data
  215. record (structure) you use in your C/C++ program exactly
  216. matches, field for field, that of the DBF record on disk.
  217.  
  218.  
  219. P: Error 240 occurs when attempting to index or reindex
  220. when all else seems to be right.
  221.  
  222. S: dBASE specifications require that fieldnames of the
  223. DBF be 0-filled.  Some programs create DBF files that do
  224. adhear to this specification, but instead just null-
  225. terminate the fieldname.  These files must be fixed
  226. before they can be used by Bullet.  When creating Bullet
  227. DBF files, you must 0-fill the fieldnames (11 bytes, 10
  228. bytes data -- byte 11 always=0).  Memset() can be used to
  229. quickly 0-fill the entire fieldlist.
  230.  
  231.  
  232. P: Find an exact match on a GetEqualXB.
  233.  
  234. S: The problem usually stems from index files created to
  235. allow non-unique keys.  Personally, I don't create index
  236. files where the primary key is not unique, but the demand
  237. was there and so Bullet provides for this.  However,
  238. since there's no real way to offer indexing on physically
  239. indentical keys, Bullet adds an enumerator word at the
  240. end of each key (only if the index file is not using
  241. unique keys).  This two enumerator is in high byte/low
  242. byte order, and is incremented for each duplicate key in
  243. the index file.  For example, if KINGM is added as a key,
  244. and is found to be the first such key by Bullet, the
  245. actual physical key is KINGM\0\0, where the enumerator is
  246. the last two bytes (in Motorola-word order for proper
  247. collating).  Upon the next insert of a "KINGM" key,
  248. Bullet uses KINGM\0\1.  And so on.
  249.  
  250. To search for this key, which by the way is 7 bytes long,
  251. and so reported by StatKXB, though logically it is only
  252. the 5 bytes (as in strcpy(keyExp,"SUBSTR(LASTN,1,5)")
  253. with non-unique keys permitted), you must account for
  254. this in your keybuffer.  For example, in your global
  255. scratch keybuffer (or you can use a separate buffer for
  256. each index file, up to you)  you must specify all 7
  257. bytes, not just the first 5.  This because the data at
  258. bytes 6/7 may not equal \0\0 or \0\1, and so these keys
  259. will not be found (error 201).  In this particular case,
  260. where duplicate keys exist, and you want to locate a
  261. KINGM record, you really have no choice but to start with
  262. strcpy(keybuff,"KINGM\0\0", be aware of the "extra" \0
  263. put by the strcpy function) and GetEqualXB, check the
  264. data record returned, and if not the one wanted, do
  265. GetNextXB until the desired record is located (or not).
  266. Alternatively, you could specify "KINGM\255\255" and use
  267. GetEqualXB followed immediately by GetPrevXB (enumerator
  268. \255\255 is the very last possible, and so will never be
  269. found, but will setup the internal gears so that the
  270. GetPrevXB will locate the very last existing "KINGM??").
  271.  
  272.  
  273. All this is really only a concern if you permit duplicate
  274. keys.  The above could be made unique by adding expanding
  275. the key expression.  For example, a key based on
  276. SUBSTR(LNAME,1,5)+SUBSTR(SSN,6,4) would be pretty unique
  277. (first 5 characters of last name followed by last 4 SSN
  278. numbers (used as characters)).
  279.  
  280.  
  281. Q: How do I specify the key I want to find?
  282.  
  283. A:  The keybuffer (AP.keybuffptr locating it) is filled
  284. with whatever data type you want to locate (hence,
  285. keybuffer is of void type).  If you want to locate a
  286. character key, simply specify the key, such as
  287. strcpy(keybuffer,"whatever") (see "Find an exact match on
  288. GetEqualXB", above).  If you are locating a binary key
  289. value, place the binary value, with the appropriate cast,
  290. into the keybuffer: e.g., *((long *)keybuffer) = 5L;.
  291.  
  292.  
  293. These problems are the most frequently expressed to me.
  294. If you are having other problems related to getting
  295. Bullet going, feel free to contact me at the locations
  296. listed under Product Support.  My ability to quickly
  297. respond to your requests is directly related to how
  298. thorough you are in expressing the exact cicumstances of
  299. any problem.
  300.  
  301. Complete sample programs are in short supply.
  302. Experienced C or C++ programmers that would like their
  303. "Bullet wrappers" possibly included in future releases
  304. are requested to contact me.  Additional samples will be
  305. made available on the BBS, available to all.  Of course,
  306. registered Bullet users will have access to the best of
  307. the best, and always have free access to the current
  308. versions of Bullet.
  309.  
  310. When you've done everything and it's still not working,
  311. it's time to read the documentation!
  312.  
  313. -------------------
  314. Questions & Answers
  315.  
  316. Q: What is BULLET?
  317.  
  318. A: BULLET is a library of program modules that together let the programmer
  319.    develop and create software that can manage huge amounts of data on
  320.    disk using the industry-standard DBF data file format. It also uses high-
  321.    speed b-tree index files to manage keyed data file access.
  322.  
  323.  
  324. Q: What compiler is BULLET for?
  325.  
  326. A: BULLET can be used by nearly all DOS compilers. It's written entirely in
  327.    assembly language. Because of this, it does not require any particular
  328.    programming language or compiler vendor. The 4 requirements are listed
  329.    in the !WHATIS.TXT file.
  330.  
  331.  
  332. Q: Why do I need BULLET when all I need to handle are small amounts of data?
  333.  
  334. A: BULLET can deal with a database as small as 1 record or as large as several
  335.    million. While your current needs may be small, your future needs are bound
  336.    to expand. BULLET can work with you now, and later, even if you switch
  337.    development platforms by moving to another compiler. And BULLET is fast,
  338.    it can deal with a database with millions of records as easily as it can
  339.    with just a few.
  340.  
  341.  
  342. Q: But b-tree stuff, isn't that hard?
  343.  
  344. A: Everything associated with maintaining the database, its data files and its
  345.    index files, is done behind-the-scenes by BULLET. You just specify how the
  346.    data record is to look by specifying the number of fields, their lengths and
  347.    types, and then specify how you want your index files to be based.
  348.  
  349.  
  350. Q: So how do I design my data record?
  351.  
  352. A: You probably have a pretty good idea, already. A good way to determine what
  353.    should go into a database is what you want to come out of it. For example,
  354.    if you're doing a mailing list program, you'll want to have at least the
  355.    name, perhaps broken into first name and last. Also you'll need the mailing
  356.    address--4 lines is usually enough, so you'll want 4 separate address line
  357.    fields. Then there's the State and ZIP, possibly even country. That's the
  358.    minimum. It would look something like this:
  359.  
  360.    FIRSTNAME field of 15 characters; LASTNAME field of 19 characters; ADDR1
  361.    field of 34 characters, ADDR2, ADDR3, ADDR4 as ADDR1; State field of 2
  362.    characters, and ZIP a field of 5 (or 9 if ZIP+4) characters. You could
  363.    specify the ZIP as a numeric field if you wanted.
  364.  
  365.    You'll notice that the longest field is 34 characters. Why? Because most
  366.    mailing labels are 3.5 inches, about 34 characters across. Since the first
  367.    name and last are usually put on the same line, their total should be 34.
  368.  
  369.    You'll probably want to add more fields like telephone number, last time
  370.    written to, oh, just about anything that you'd think would be important to
  371.    know. There you go, you've just designed your data record.
  372.  
  373.  
  374. Q: Then what?
  375.  
  376. A: Then decide how you need to access this data. You'll want to access it at
  377.    least by name, so one index you'll want is on the name. While you could
  378.    specify the entire name be used as a key, say LASTNAME+FIRSTNAME, this is
  379.    a bit of overkill. Instead, you may want to use just a portion of the name.
  380.    A good candidate would be SUBSTR(LASTNAME,1,5)+SUBSTR(FIRSTNAME,1,1). This
  381.    sets up a key that's only 6 bytes long. The first method, using all the
  382.    name, would be 34 bytes long. By keeping your keys short you'll keep your
  383.    index files small and your index performance high. And yes, you can also
  384.    mix numeric field types with character field types in your key expressions.
  385.  
  386.  
  387. Q: But what if I have two or more names that are identical, or very similar
  388.    but have these parts of the names the same?
  389.  
  390. A: BULLET lets you specify if your index files allow only unique key entries or
  391.    whether duplicate keys are permitted. When keying on a name you should have
  392.    your index file allow duplicate keys. What BULLET does is number these
  393.    identical keys by adding a suffix to each key (called an enumerator). This
  394.    enumerator allows the index algorithm to treat each key as a different key.
  395.    If you search the index for all matches in the first 6 characters of the
  396.    key (the enumerators will always be different) these similar names will
  397.    be found in consecutive order. To find out if the key you've just accessed
  398.    is the actual person you had in mind, you'd scan the data record associated
  399.    with that key for other information, such as middle initial, address,
  400.    anything that would make that person recognizable from another with a
  401.    similar key. If it isn't what you're looking for, get the next key and data
  402.    record, and so on until the first 6 characters of the key no longer are the
  403.    6 you're looking for.
  404.  
  405.  
  406. Q: So I've got my data record designed and also my primary index file. What
  407.    else should I do?
  408.  
  409. A: Now that the database is designed most of the "unknown" is taken care of.
  410.    What comes next depends on how you, yourself, program. What I often do
  411.    next is detail exactly what I want the output to be. That way, I've got
  412.    the front and the back and just need to do the middle. The middle is where
  413.    the fun's at. You'll be amazed at just how few of your in-the-middle coding
  414.    is spent on managing the database. BULLET takes care of all the little
  415.    details. You just need to give it the data and tell it what to do with
  416.    it. Or you tell it what to get and it comes back with what you requested.
  417.    You then do whatever you want with that data.
  418.  
  419.  
  420. Q: I've looked at the header file and it sure has a lot of commands. You're
  421.    telling me that this is simple?
  422.  
  423. A: Yes. Once you've created your data and index files, those mid-level
  424.    routines are not often used. Almost everything you do in your in-the-middle
  425.    coding will use the high-level routines. The InsertXB and UpdateXB routines
  426.    handle adding new or changing existing data, and the GetFirstXB, GetNextXB,
  427.    etc., routines handle getting the data. 90% of the time these are the
  428.    routines your program will be using.
  429.  
  430.  
  431. Q: What about all those packs? How can I keep them straight?
  432.  
  433. A: The good thing about modern programming langauges is that they let you
  434.    build reusable code. The ideal way to use BULLET is to build reusable
  435.    code objects in your programming langauge of choice and hide the down-and-
  436.    dirty aspects of dealing with the various packs in those objects. For
  437.    example, a create key routine could be written once and that object used
  438.    for all your other programming projects:
  439.  
  440.       DECLARE FUNCTION CreateNewIndex% (filen$, kx$, kflags%, XBlink%)
  441.  
  442.       FUNCTION CreateNewIndex(filename$, kx$, keyflags, XBlink)
  443.  
  444.       DIM CKP AS CreateKeyPack
  445.       DIM TmpFile AS STRING * 80
  446.       DIM TmpExp AS STRING * 136
  447.  
  448.       TmpFile = filename$ + CHR$(0)
  449.       TmpExp = kx$ + CHR$(0)
  450.  
  451.       CKP.Func = CreateKXB
  452.       CKP.FilenamePtrOff = VARPTR(TmpFile)
  453.       CKP.FilenamePtrSeg = VARSEG(TmpFile)
  454.       CKP.KeyExpPtrOff = VARPTR(TmpExp)
  455.       CKP.KeyExpPtrSeg = VARSEG(TmpExp)
  456.       CKP.XBlink = XBlink
  457.       CKP.keyflags = keyflags
  458.       CKP.CodePageID = -1
  459.       CKP.CountryCode = -1
  460.       CKP.CollatePtrOff = 0
  461.       CKP.CollatePtrSeg = 0
  462.       stat = BULLET(CKP)
  463.       CreateNewIndex = stat
  464.  
  465.       END FUNCTION
  466.  
  467.    The TmpFile and TmpExp were used in this QuickBASIC example so that the
  468.    string data is assigned to a fixed-length string. VARSEG/VARPTR operate
  469.    on fixed-length strings identically in both BASIC 7 and QuickBASIC 4.5.
  470.    If you want to forgo this compatibility you could use a var-len string
  471.    and the BASIC SADD() function instead of VARPTR. I find using the
  472.    temporary fixed-length strings just as easy since a CHR$(0) needs to
  473.    be appended to the string in any case. And they are temporary strings--
  474.    as soon as the CreateNewIndex() routine exits, the memory assigned to the
  475.    Tmp strings is released.
  476.  
  477.    So, once you write this create index object, you can reuse it over and
  478.    over again without needing to recode it every time. Just a simple
  479.  
  480.    stat = CreateNewIndex(filename$,keyexpression$,keyflags,XBlink)
  481.  
  482.    is all you need to put in your in-the-middle code. Oh, the key expression,
  483.    keyflags, and XBlink are all explained under CreateKXB in CZ.
  484.  
  485.  
  486. Q: I think I'm getting the hang of it. What's next?
  487.  
  488. A: Jump in and start coding. You may want to look over, maybe even print out,
  489.    one of the example programs. The BB_CAI10.BAS is straight forward, try
  490.    that. If you have any questions, just pop-up CZ. It's all in there.
  491.  
  492.  
  493. Q: Sounds good. But, tell me, what is the first thing I'm likely to muff?
  494.  
  495. A: Nothing serious. Just make sure that your record structure in memory
  496.    reserves the first byte for the delete tag. Also, make sure that the field
  497.    descriptors you assigned when you created the data file match your in-memory
  498.    structure, i.e., if you've created a data file (using CreateDXB) with say, 3
  499.    fields, each 25 characters long, make sure that your in-memory structure is
  500.    also 3 fields, each 25 characters:
  501.  
  502.       TYPE ExampleRecordTYPE
  503.       tag AS STRING * 1
  504.       Field1 AS STRING * 25
  505.       Field2 AS STRING * 25
  506.       Field3 AS STRING * 25
  507.       END TYPE
  508.  
  509.    Note: it is allowable to alias your physical fields but the total length
  510.    must match the total length of the DBF data record. (Alias meaning that
  511.    instead of using Field1 AS STRING * 25, you use Field1a AS STRING 13 and
  512.    Field1b AS STRING 12. Be sure you know what you're doing!--the IDEMO.BAS
  513.    program uses this technique (not in the strict sense of alias) by using a
  514.    single 4000-byte in-memory record since it can't know beforehand what
  515.    structure a random DBF file has.)
  516.  
  517.    Also, the transaction routines (InsertXB, UpdateXB, ReindexXB, and the
  518.    LockXBs) return a transaction index rather than a completion code. Be sure
  519.    to check the documentation for the routines.
  520.  
  521. Q: I'm off.
  522.  
  523. A: So am I, but be sure to...
  524.  
  525.    ...Read the manual! Until you do you can't take full advantage of BULLET.
  526.  
  527.  
  528. ---------------
  529. What is BULLET?
  530.  
  531. If you've been wanting to create the ultimate database program, or just a simple
  532. mailing list to handle your holiday cards, BULLET is the programming library for
  533. you. BULLET is a library module for programmers developing in MS-DOS. What
  534. language you ask? Would you believe most any? That's right, BULLET is designed
  535. to operate as-is with most compilers* available for DOS (DOS 3.3 or above is
  536. required). This is possible because BULLET is written entirely in assembly
  537. langauge. Everything it needs it has built in, so no specific compiler run-time
  538. library is required. Your compiler simply needs to be able to:
  539.  
  540.  1. Perform a far call to an external routine using the Pascal calling
  541.     convention.
  542.  2. Build a parameter pack, as in a C struct or BASIC TYPE, for example.
  543.  3. Pass a single far pointer by value (of the parameter pack) to the external
  544.     routine.
  545.  4. Accept a return integer value from the external routine (optional).
  546.  
  547. BULLET uses high-speed b-tree indexes and the industry-standard DBF data file
  548. format to quickly and easily move data to and from disk. The BULLET API includes
  549. over 65 functions to perform tasks from low-level direct DOS file I/O to
  550. high-level transaction-based updates to network routines that let you control
  551. who has access to your files and when. National Language Support (NLS) is built
  552. into each key file. This allows you to properly sort mixed-case and/or foreign
  553. language alphabets. BULLET supports character keys up to 64 bytes as well as
  554. 16-/32-bit integer keys (signed or unsigned). Index files specify if duplicate
  555. keys are allowed or whether unique keys are to be enforced. And although dBASE
  556. DBF data file compatiblility is standard, you can create non-standard DBF files,
  557. such as having fields contain binary data. Key expressions are specified using
  558. text, as in kx = "SUBSTR(Fieldname,1,10)+Fieldname+SUBSTR(Fieldname,10,3)". Keys
  559. may be composed of up to 16 separate fields, located anywhere within the record.
  560.  
  561. The transaction nature of BULLET greatly simplifies what is required of the
  562. programmer. For example, let's say you have a 100,000-record data file with 12
  563. active index files and need to insert a new record. With BULLET, you simply
  564. build the data record and call the InsertXB routine. InsertXB adds the record to
  565. the data file, builds all 12 keys and inserts each into the appropriate index
  566. file. If an error occured, say, while inserting the 11th key, BULLET
  567. automatically backs-out all changes just made to the key files and data file. As
  568. another example, say, updating an exisiting data record, you'd make any
  569. modifications to the data record and call the UpdateXB routine. UpdateXB updates
  570. the record in the data file and automatically updates all 12 index files. If an
  571. error occured, say, with the 5th key, BULLET automatically backs-out all changes
  572. made to the key files and data file.  In addition, transaction-based network
  573. routines are available. These high-level lock/unlock routines also automatically
  574. handle the necessary reloading and flushing of file headers. And what  about
  575. performance? Read on.
  576.  
  577. BULLET is fast! The ReindexXB routine indexes your DBF data files in no time
  578. flat: that 100,000 record .DBF can be completely reindexed in under 30 seconds
  579. on a fast computer system. The InsertXB routine can add 1,000 new records and
  580. keys to that 100,000 record database at a rate of over 50 new inserts per
  581. second. This isn't inserting into an empty database, it's inserting into a
  582. database that's already 100,000 records large. UpdateXB can update
  583. 1,000 records in that database at a rate of 20 updates/second. And what about
  584. access speed? How fast can you get to your data once it's in the database? Real
  585. fast! To access from first key to last all 100,000 keys takes less than
  586. 25 seconds (4500+/sec). That shows how fast you can find keys. To access the
  587. keys and also access each data record associated with that key takes a bit
  588. longer. Accessing all 100,000 keys and records takes just over a minute
  589. (1400+/sec). This is in-order access. Incredible. And if you wanted to access
  590. from the last key to the first (reverse-order), the times are just as fast.
  591. What do you get with the BULLET package?
  592.  
  593.  - The BULLET library module, 19K of code and static data space (small indeed)
  594.  - Sample program in C plus an interactive demo for browsing any DBF
  595.  - 200K of documentation, source examples, and a tutorial, all online
  596.  - CZ, the 15K online, context-sensitive, hyper-text help manager
  597.  - Royalty-free use of BULLET in your programs
  598.  
  599. Routines by category:
  600.  
  601. SYSTEM: InitXB, ExitXB, AtExitXB, MemoryXB, BreakXB, BackupFileXB, StatHandleXB,
  602. GetExtErrorXB
  603.  
  604. MID-LEVEL RECORD/KEY ACCESS: CreateDXB, OpenDXB, CloseDXB, StatDXB, ReadDHXB,
  605. FlushDHXB, CopyDHXB, ZapDHXB, CreateKXB, OpenKXB, CloseKXB, StatKXB, ReadKHXB,
  606. FlushKHXB, CopyKHXB, ZapKHXB, GetDescriptorXB, GetRecordXB, AddRecordXB,
  607. UpdateRecordXB, DeleteRecordXB, UndeleteRecordXB, PackRecordsXB, FirstKeyXB,
  608. EqualKeyXB, NextKeyXB, PrevKeyXB, LastKeyXB, StoreKeyXB, DeleteKeyXB,
  609. BuildKeyXB, CurrentKeyXB
  610.  
  611. HIGH-LEVEL ACCESS: GetFirstXB, GetEqualXB, GetNextXB, GetPrevXB, GetLastXB,
  612. InsertXB, UpdateXB, ReindexXB
  613.  
  614. NETWORK: LockXB, UnlockXB, LockKeyXB, UnlockKeyXB, LockDataXB, UnlockDataXB,
  615. DriveRemoteXB, FileRemoteXB, SetRetriesXB
  616.  
  617. LOW-LEVEL DOS ACCESS: DeleteFileDOS, RenameFileDOS, CreateFileDOS,
  618. AccessFileDOS, OpenFileDOS, SeekFileDOS, ReadFileDOS, ExpandFileDOS,
  619. WriteFileDOS, CloseFileDOS, MakeDirDOS
  620.  
  621.